---
title: .family
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

Prima di leggere questa sezione, considera di leggere [providers](/docs/concepts/providers) e [come leggerli](/docs/concepts/reading). 
In questa sezione parleremo in dettaglio del modificatore di provider `.family`.

Il modificatore `.family` ha uno scopo: ottenere un unico provider basato su parametri esterni.

Alcuni casi di uso comune di `family` sono:

- Combinare [FutureProvider] con `.family` per ottenere (fetch) un `Message` dal suo ID.
- Passare il `Locale` corrente ad un provider, cosicchè si possano gestire le traduzioni.

## Uso

Il modo in cui funzionano le famiglie consiste nell'aggiungere un parametro aggiuntivo al 
provider. Questo parametro può essere poi liberamente usato nel nostro provider per creare 
uno stato.

Per esempio, potremmo combinare `family` con [FutureProvider] per ottenere un `Message` dal suo ID:

```dart
final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
  return dio.get('http://my_api.dev/messages/$id');
});
```

Successivamente, quando andremo ad usare il provider `messagesFamily`, la sintassi sarà 
leggermente differente dalla solita.
La sintassi abituale non funzionerà più:

```dart
Widget build(BuildContext context, WidgetRef ref) {
  // Errore – messagesFamily non è un provider
  final response = ref.watch(messagesFamily);
}
```

Dovremo passare invece un parametro a `messagesFamily`:

```dart
Widget build(BuildContext context, WidgetRef ref) {
  final response = ref.watch(messagesFamily('id'));
}
```

:::info
É possibile usare una famiglia con diversi parametri simultaneamente. 
Ad esempio, potremmo usare un `titleFamily` per leggere entrambe le traduzioni francesi e 
inglesi nello stesso momento:

```dart
@override
Widget build(BuildContext context, WidgetRef ref) {
  final frenchTitle = ref.watch(titleFamily(const Locale('fr')));
  final englishTitle = ref.watch(titleFamily(const Locale('en')));

  return Text('fr: $frenchTitle en: $englishTitle');
}
```

:::

## Restrizioni dei parametri

Affinchè le famiglie possano funzionare correttamente, è cruciale per il parametro passato 
al provider di avere un coerente `hashCode` e `==`.

Idealmente, il parametro potrebbe essere sia un tipo primitivo (bool/int/String), una costante (i provider) oppure un oggetto immutabile che sovrascrive `==` e `hashCode`.

### _PREFERIRE_ l'uso di `autoDispose` quando il parametro non è una constante:

Potresti voler usare le famiglie per passare l'input di un campo di ricerca al tuo provider. 
Ma quel valore potrebbe cambiare spesso e non essere più utilizzato.  
Ciò può causare memory leaks siccome, per default, un provider non è mai distrutto anche se non più usato.

Usare sia `.family` che `.autoDispose` ci permette di evitare quella perdita di memoria (memory leak).

```dart
final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
  return fetchCharacters(filter: filter);
});
```

## Passare più parametri ad una famiglia

Le famiglie non hanno un supporto costruito internamente per passare più valori ad un provider.

D'altro canto però, quel valore potrebbe essere _qualsiasi_ oggetto (purchè corrisponda alle 
restrizioni menzionate precedentemente).

Possiamo dunque passare più parametri tramite:

- Una tupla di [tupla](http://pub.dev/packages/tuple).
- Oggetti generati con [Freezed] o [built_value](https://pub.dev/packages/built_value).
- Oggetti che usano [equatable](https://pub.dev/packages/equatable).

Di seguito un esempio che usa [Freezed] o [equatable]:

<Tabs
  groupId="family"
  defaultValue="freezed"
  values={[
    { label: 'Freezed', value: 'freezed', },
    { label: 'Equatable', value: 'equatable', },
  ]}
>

<TabItem value="freezed">

```dart
@freezed
abstract class MyParameter with _$MyParameter {
  factory MyParameter({
    required int userId,
    required Locale locale,
  }) = _MyParameter;
}

final exampleProvider = Provider.autoDispose.family<Something, MyParameter>((ref, myParameter) {
  print(myParameter.userId);
  print(myParameter.locale);
  // Logica usando userId/locale
});

@override
Widget build(BuildContext context, WidgetRef ref) {
  int userId; // Legge l'ID utente da qualche parte
  final locale = Localizations.localeOf(context);

  final something = ref.watch(
    exampleProvider(MyParameter(userId: userId, locale: locale)),
  );

  ...
}
```

</TabItem>
<TabItem value="equatable">

```dart
class MyParameter extends Equatable  {
  MyParameter({
    required this.userId,
    required this.locale,
  });

  final int userId;
  final Locale locale;

  @override
  List<Object> get props => [userId, locale];
}

final exampleProvider = Provider.family<Something, MyParameter>((ref, myParameter) {
  print(myParameter.userId);
  print(myParameter.locale);
  // Logica usando userId/locale
});

@override
Widget build(BuildContext context, WidgetRef ref) {
  int userId; // Legge l'ID utente da qualche parte
  final locale = Localizations.localeOf(context);

  final something = ref.watch(
    exampleProvider(MyParameter(userId: userId, locale: locale)),
  );

  ...
}
```

</TabItem>
</Tabs>

[freezed]: https://pub.dev/packages/freezed
[provider]: https://pub.dev/documentation/riverpod/latest/riverpod/Provider-class.html
[equatable]: https://pub.dev/packages/equatable
[futureprovider]: ../../providers/future_provider